/**
 * Copyright 2010 Per-Erik Bergman (per-erik.bergman@jayway.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.jayway.opengl.tutorial.mesh;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Bitmap;
import android.opengl.GLUtils;

/**
 * Mesh is a base class for 3D objects making it easier to create and maintain
 * new primitives.
 * 
 * @author Per-Erik Bergman (per-erik.bergman@jayway.com)
 * 
 */
public class Mesh {
	boolean hasColor;
	boolean hasTexCoords;
	int vertexSize;
	IntBuffer vertices;
	int[] tmpBuffer;
	ShortBuffer indices;
	boolean hasNormals;
	float angle = 0;
	// Our vertex buffer.
	private final FloatBuffer mVerticesBuffer = null;

	// Our index buffer.
	private final ShortBuffer mIndicesBuffer = null;

	// Our UV texture buffer.
	private FloatBuffer mTextureBuffer; // New variable.

	// Our texture id.
	private int mTextureId = -1; // New variable.

	// The bitmap we want to load as a texture.
	private Bitmap mBitmap; // New variable.

	// Indicates if we need to load the texture.
	// private boolean mShouldLoadTexture = false; // New variable.

	// The number of indices.
	private final int mNumOfIndices = -1;

	// Flat Color
	private final float[] mRGBA = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };

	// Smooth Colors
	private FloatBuffer mColorBuffer = null;

	// Translate params.
	public float x = 0;

	public float y = 0;

	public float z = 0;

	// Rotate params.
	public float rx = 0;

	public float ry = 0;

	public float rz = 0;

	/**
	 * Render the mesh.
	 * 
	 * @param gl
	 *            the OpenGL context to render to.
	 */
	public void draw(GL10 gl) {
		// gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		// vertices.position(0);
		// gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, vertices);
		//
		// if (hasColor) {
		// gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
		// vertices.position(3);
		// gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices);
		// }
		//
		// if (hasTexCoords) {
		// gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		// vertices.position(hasColor ? 7 : 3);
		// gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices);
		// }
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		vertices.position(0);
		gl.glVertexPointer(3, GL10.GL_FLOAT, vertexSize, vertices);

		if (hasColor) {
			gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
			vertices.position(3);
			gl.glColorPointer(4, GL10.GL_FLOAT, vertexSize, vertices);
		}

		if (hasTexCoords) {
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			vertices.position(hasColor ? 7 : 3);
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices);
		}
		gl.glTranslatef(0, 0, -3);
		gl.glRotatef(angle, 0, 1, 0);

		if (indices != null) {
			indices.position(0);
			gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_SHORT,
					indices);
		} else {
			gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 36);
		}

		if (hasTexCoords)
			gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

		if (hasColor)
			gl.glDisableClientState(GL10.GL_COLOR_ARRAY);

	}

	/**
	 * Set the vertices.
	 * 
	 * @param vertices
	 */
	protected void setVertices(float[] vertices) {
		this.vertices.clear();
		int len = 0 + vertices.length;
		for (int i = 0, j = 0; i < len; i++, j++)
			tmpBuffer[j] = Float.floatToRawIntBits(vertices[i]);
		this.vertices.put(tmpBuffer, 0, vertices.length);
		this.vertices.flip();
	}

	/**
	 * Set the indices.
	 * 
	 * @param indices
	 */
	protected void setIndices(short[] indices) {
		this.indices.clear();
		this.indices.put(indices, 0, indices.length);
		this.indices.flip();
	}

	/**
	 * Set the texture coordinates.
	 * 
	 * @param textureCoords
	 */
	protected void setTextureCoordinates(float[] textureCoords) { // New
		// function.
		// float is 4 bytes, therefore we multiply the number if
		// vertices with 4.
		ByteBuffer byteBuf = ByteBuffer
				.allocateDirect(textureCoords.length * 4);
		byteBuf.order(ByteOrder.nativeOrder());
		mTextureBuffer = byteBuf.asFloatBuffer();
		mTextureBuffer.put(textureCoords);
		mTextureBuffer.position(0);
	}

	/**
	 * Set one flat color on the mesh.
	 * 
	 * @param red
	 * @param green
	 * @param blue
	 * @param alpha
	 */
	protected void setColor(float red, float green, float blue, float alpha) {
		mRGBA[0] = red;
		mRGBA[1] = green;
		mRGBA[2] = blue;
		mRGBA[3] = alpha;
	}

	/**
	 * Set the colors
	 * 
	 * @param colors
	 */
	protected void setColors(float[] colors) {
		// float has 4 bytes.
		ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4);
		cbb.order(ByteOrder.nativeOrder());
		mColorBuffer = cbb.asFloatBuffer();
		mColorBuffer.put(colors);
		mColorBuffer.position(0);
	}

	/**
	 * Set the bitmap to load into a texture.
	 * 
	 * @param bitmap
	 */
	public int loadBitmap(GL10 gl, Bitmap bitmap) { // New function.
		this.mBitmap = bitmap;
		// New part...
		return loadGLTexture(gl);

	}

	/**
	 * Loads the texture.
	 * 
	 * @param gl
	 */
	private int loadGLTexture(GL10 gl) { // New function
		// Generate one texture pointer...
		int[] textures = new int[1];
		gl.glGenTextures(1, textures, 0);
		mTextureId = textures[0];

		// ...and bind it to our array
		gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureId);
		// Use the Android GLUtils to specify a two-dimensional texture image
		// from our bitmap
		GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0);
		// Create Nearest Filtered Texture
		// gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
		// GL10.GL_LINEAR);
		// gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
		// GL10.GL_LINEAR);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
				GL10.GL_NEAREST);
		gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
				GL10.GL_NEAREST);

		// // Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
		// gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
		// GL10.GL_CLAMP_TO_EDGE);
		// gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
		// GL10.GL_REPEAT);

		gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
		mBitmap.recycle();
		return mTextureId;
	}

	public void update(float deltaTime) {
		angle += 45 * deltaTime;

	}
}
